package oakclub.service;

import oakclub.entity.MessageEntity;
import oakclub.entity.RosterEntity;
import oakclub.entity.UserEntity;
import oakclub.global.Config;
import oakclub.global.Container;
import oakclub.global.DateFormatter;
import oakclub.model.ChatModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileWriter;
import java.util.*;
import java.util.regex.Pattern;

public class ChatService
{
    private final String messageDir = Config.get("media.dir") + "/message";

    private final String CHAT_LOG_FIELD_SEPARATE = "|&|";
    private final String CHAT_LOG_MESSAGE_SEPARATE = "|\n|";
    private final String CHAT_LOG_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    private final int CHAT_HISTORY_LIMIT = 50;

    private final Logger logger = LoggerFactory.getLogger(ChatService.class);

    public void addRoster(long userId, long targetId) throws Exception
    {
        ChatModel chatModel = (ChatModel) Container.getComponent(ChatModel.class);
        chatModel.addRoster(userId, targetId);
    }

    public List<RosterEntity> getChatList(long userId) throws Exception
    {
        ChatModel chatModel = (ChatModel) Container.getComponent(ChatModel.class);
        return chatModel.getChatList(userId);
    }

    public void readMessage(long userId, long targetId) throws Exception
    {
        ChatModel chatModel = (ChatModel) Container.getComponent(ChatModel.class);
        chatModel.readMessage(userId, targetId);
    }

    private File getHistoryFile(long id1, long id2, Date date, boolean isCreatedIfNotExist) throws Exception
    {
        // swap
        if(id1 > id2)
        {
            long tmp = id1; id1 = id2; id2 = tmp;
        }

        String folderPath = this.messageDir + '/' + DateFormatter.getFormatter("yyyy-MM-dd").format(date);
        String filePath = folderPath + '/' + id1 + '_' + id2 + ".log";
        if(!isCreatedIfNotExist)
        {
            return new File(filePath);
        }

        // folder
        File folder = new File(folderPath);
        if(!folder.exists())
        {
            if(!folder.mkdir() || !folder.setReadable(true) || !folder.setWritable(true))
            {
                throw new IllegalStateException("create log chat folder failed");
            }
        }

        // file
        File file = new File(filePath);
        if(!file.exists())
        {
            if(!file.createNewFile() || !file.setReadable(true) || !file.setWritable(true))
            {
                throw new IllegalStateException("create log chat folder failed");
            }
        }

        return file;
    }

    public void sendMessage(long userId, long targetId, String msg) throws Exception
    {
        // check blocked
        BlockService blockService = (BlockService) Container.getComponent(BlockService.class);
        if(blockService.isBlocked(userId, targetId))
        {
            return;
        }

        // add history chat
        Date now = new Date();
        File file = this.getHistoryFile(userId, targetId, now, true);
        FileWriter writer = new FileWriter(file, true);
        String log = userId + this.CHAT_LOG_FIELD_SEPARATE +
                     targetId + this.CHAT_LOG_FIELD_SEPARATE +
                     DateFormatter.getFormatter(this.CHAT_LOG_TIME_PATTERN).format(now) + this.CHAT_LOG_FIELD_SEPARATE +
                     msg + this.CHAT_LOG_MESSAGE_SEPARATE;
        writer.write(log);
        writer.flush();

        // update db
        ChatModel chatModel = (ChatModel) Container.getComponent(ChatModel.class);
        chatModel.sendMessage(userId, targetId, msg);

        // push notification
        NotificationService notificationService = (NotificationService) Container.getComponent(NotificationService.class);
        notificationService.pushChatNotification(userId, targetId, msg);

        // log
        ScribeService scribeService = (ScribeService) Container.getComponent(ScribeService.class);
        scribeService.chat(userId);
        if(msg.contains("type=\"sticker\""))
        {
            String image = msg.replaceAll(".*src.*/([^\"]*)\".*", "$1");
            scribeService.sendSticker(userId, targetId, image);
        }
    }

    public List<MessageEntity> getHistory(long userId, long targetId, int numLastDay, boolean isLimit) throws Exception
    {
        if(numLastDay < 1)
        {
            throw new IllegalArgumentException("num last day must greater than zero");
        }

        // roster
        ChatModel chatModel = (ChatModel) Container.getComponent(ChatModel.class);
        RosterEntity roster = chatModel.getRoster(userId, targetId);

        // pattern regex
        String fsRegex = this.CHAT_LOG_FIELD_SEPARATE.replaceAll("\\|", "\\\\\\|");
        String msRegex = this.CHAT_LOG_MESSAGE_SEPARATE.replaceAll("\\|", "\\\\\\|");
        Pattern pattern = Pattern.compile(fsRegex + '|' + msRegex);

        // calendar
        Calendar calendar = Calendar.getInstance();
        if(roster.getLastMessageTime() != null)
        {
            calendar.setTime(roster.getLastMessageTime());
        }
        calendar.add(Calendar.DATE, -(numLastDay-1));

        // get history from file
        List<MessageEntity> messages = new ArrayList<>();
        for(int i = 0; i < numLastDay; i++)
        {
            try
            {
                File file = this.getHistoryFile(userId, targetId, calendar.getTime(), false);
                if(file.exists())
                {
                    Scanner scanner = new Scanner(file);
                    scanner.useDelimiter(pattern);
                    while(scanner.hasNext())
                    {
                        try
                        {
                            UserEntity user = new UserEntity();
                            user.setId(scanner.nextLong());

                            UserEntity target = new UserEntity();
                            target.setId(scanner.nextLong());

                            MessageEntity message = new MessageEntity();
                            message.setUser(user);
                            message.setTarget(target);
                            message.setTime(DateFormatter.getFormatter(this.CHAT_LOG_TIME_PATTERN).parse(scanner.next()));
                            message.setContent(scanner.next());
                            messages.add(message);
                        }
                        catch(Exception e)
                        {
                            this.logger.warn("parse chat log failed", e);
                        }
                    }
                }
            }
            catch(Exception e)
            {
                this.logger.warn("get chat file failed", e);
            }
            calendar.add(Calendar.DATE, 1);
        }

        // litmit
        int numMessage = messages.size();
        int limitMessage = this.CHAT_HISTORY_LIMIT;
        if(isLimit && numMessage > limitMessage)
        {
            messages = messages.subList(limitMessage-numMessage, limitMessage);
        }

        return messages;
    }

    public List<MessageEntity> getHistory(long userId, long targetId) throws Exception
    {
        return this.getHistory(userId, targetId, 2, true);
    }
}
